// 版权所有2011 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 该文件实现了Check函数，该函数驱动类型检查。

package types2

import (
	"cmd/compile/internal/syntax"
	"errors"
	"fmt"
	"go/constant"
)

var nopos syntax.Pos

// 调试/开发支持
const debug = false // 在开发过程中保持打开

// exprInfo存储有关非类型表达式的信息。
type exprInfo struct {
	isLhs bool // 表达式是带延迟类型检查的移位的lhs操作数
	mode  operandMode
	typ   *Basic
	val   constant.Value // 常量值；或者nil（如果不是常数）
}

// 环境表示对象所在的环境
// 类型已检查。
type environment struct {
	decl          *declInfo                 // 其初始表达式/函数体被选中的包级声明
	scope         *Scope                    // 查找的最顶端范围
	pos           syntax.Pos                // 如果有效，标识符将被查找，就像在位置pos（由Eval使用）
	iota          constant.Value            // 常量声明中的iota值一样；nil否则
	errpos        syntax.Pos                // 如果有效，则带有继承初始值设定项的常数的标识符位置
	inTParamList  bool                      // 如果在类型参数列表中设置
	sig           *Signature                // 如果在函数中，则函数签名；nil否则
	isPanic       map[*syntax.CallExpr]bool // 一组紧急调用表达式（用于终止检查）
	hasLabel      bool                      // 如果函数使用标签，则设置（仅占函数的1%）；未使用的外部函数
	hasCallOrRecv bool                      // 如果表达式包含函数调用或通道接收操作，则设置
}

// 查找在当前环境中查找名称并返回匹配的对象，或为零。
func (env *environment) lookup(name string) Object {
	_, obj := env.scope.LookupParent(name, env.pos)
	return obj
}

// importKey通过导入路径和源目录
// （包含导入文件的目录）标识导入的包。实际上，目录
// 可能总是相同的，也可能无关紧要。给定（导入路径，目录），导入器必须始终返回相同的包（但给定两个不同的导入路径，导入器仍然可以通过将它们映射到相同的包
// 路径来返回相同的包）。
type importKey struct {
	path, dir string
}

// dotImportKey描述给定范围内的点导入对象。
type dotImportKey struct {
	scope *Scope
	name  string
}

// 一个动作描述了一个（延迟的）动作。
type action struct {
	f    func()      // 要执行的操作
	desc *actionDesc // 操作说明；可能为零，需要设置debug 
}

// 如果设置了debug，descripbef将为操作a设置printf格式的描述。
// 否则，这是不可操作的。
func (a *action) describef(pos poser, format string, args ...interface{}) {
	if debug {
		a.desc = &actionDesc{pos, format, args}
	}
}

// actionDesc提供有关操作的信息。
// 仅用于调试。
type actionDesc struct {
	pos    poser
	format string
	args   []interface{}
}

// 检查程序维护类型检查程序的状态。
// 必须使用NewChecker创建。
type Checker struct {
	// 包信息
	// （由NewChecker初始化，在checker的生命周期内有效）
	conf *Config
	ctxt *Context // 用于消除重复实例的上下文
	pkg  *Package
	*Info
	version version                // 接受语言版本
	nextID  uint64                 // 类型参数的唯一Id（第一个有效Id为1）
	objMap  map[Object]*declInfo   // 将包级对象和（非接口）方法映射到声明信息
	impMap  map[importKey]*Package // 将（导入路径、源目录）映射到（完整或假）包
	infoMap map[*Named]typeInfo    // 将命名类型映射到其关联的类型信息（用于循环检测）

	// pkgPathMap将包名称映射到我们在导入图中的任意位置看到的该名称的一组不同导入路径。它用于
	// 消除错误消息中包名的歧义。
	// 
	// pkgPathMap是惰性分配的，因此我们不必为在快乐的道路上构建
	// 付出代价。请参阅NPKGmap跟踪我们已经
	// 浏览过的软件包。
	pkgPathMap map[string]map[string]bool
	seenPkgMap map[*Package]bool

	// 对一组包文件进行类型检查时收集的信息
	// （由文件初始化，仅在检查期间有效。文件；
	// 地图和列表按需分配）
	files         []*syntax.File              // 包文件列表
	imports       []*PkgName                  // 导入包列表
	dotImportMap  map[dotImportKey]*PkgName   // 将导入的对象映射到包它们是通过
	recvTParamMap map[*syntax.Name]*TypeParam // 将空白的接收器类型参数映射到它们的类型
	brokenAliases map[*TypeName]bool          // 一组具有已断开（尚未确定）类型的别名
	unionTypeSets map[*Union]*_TypeSet        // 联合类型的计算类型集
	mono          monoGraph                   // 用于检测非单形实例化循环的图

	firstErr error                    // 遇到的第一个错误
	methods  map[*TypeName][]*Func    // 映射包关联的非空（非接口）方法的作用域类型名称
	untyped  map[syntax.Expr]exprInfo // 没有最终类型的表达式映射
	delayed  []action                 // 延迟动作段堆栈；段按FIFO顺序处理
	objPath  []Object                 // 在类型推断（用于周期报告）
	cleaners []cleaner                // 类型检查结束时可能需要最终清理的类型列表

	// 检查当前对象类型的环境（仅在特定对象的类型检查期间有效
	// 
	environment

	// 调试
	indent int // 跟踪
}

// addDeclDep添加依赖项边缘（check.decl->to）如果选中。decl存在
func (check *Checker) addDeclDep(to Object) {
	from := check.decl
	if from == nil {
		return // 不在包级别的初始化表达式
	}
	if _, found := check.objMap[to]; !found {
		return // to不是包级别的对象
	}
	from.addDep(to)
}

// 代理alias记录的类型尚未确定。
// 它还设置别名。典型到典型[无效]。
func (check *Checker) brokenAlias(alias *TypeName) {
	if check.brokenAliases == nil {
		check.brokenAliases = make(map[*TypeName]bool)
	}
	check.brokenAliases[alias] = true
	alias.typ = Typ[Invalid]
}

// validAlias记录别名具有有效的类型typ（可能是typ[Invalid]）。
func (check *Checker) validAlias(alias *TypeName, typ Type) {
	delete(check.brokenAliases, alias)
	alias.typ = typ
}

// isBrokenAlias报告alias是否还没有确定的类型。
func (check *Checker) isBrokenAlias(alias *TypeName) bool {
	return alias.typ == Typ[Invalid] && check.brokenAliases[alias]
}

func (check *Checker) rememberUntyped(e syntax.Expr, lhs bool, mode operandMode, typ *Basic, val constant.Value) {
	m := check.untyped
	if m == nil {
		m = make(map[syntax.Expr]exprInfo)
		check.untyped = m
	}
	m[e] = exprInfo{lhs, mode, typ, val}
}

// later将f推到稍后处理的操作堆栈中；
// 在当前语句的末尾，或者在局部常量
// 或变量声明的情况下，在常量或变量在范围
// 之前（这样f仍然可以在任何新声明之前看到范围）。
// 稍后返回推送的操作，这样就可以通过操作提供描述
// 了。如果需要，请描述调试。
func (check *Checker) later(f func()) *action {
	i := len(check.delayed)
	check.delayed = append(check.delayed, action{f: f})
	return &check.delayed[i]
}

// push将obj推到对象路径上，并返回其在路径中的索引。
func (check *Checker) push(obj Object) int {
	check.objPath = append(check.objPath, obj)
	return len(check.objPath) - 1
}

// 弹出并返回对象路径中最顶端的对象。
func (check *Checker) pop() Object {
	i := len(check.objPath) - 1
	obj := check.objPath[i]
	check.objPath[i] = nil
	check.objPath = check.objPath[:i]
	return obj
}

type cleaner interface {
	cleanup()
}

// needsCleanup记录实现清理方法
// 的对象/类型，该方法将在类型检查结束时调用。
func (check *Checker) needsCleanup(c cleaner) {
	check.cleaners = append(check.cleaners, c)
}

// NewChecker为给定包返回一个新的Checker实例。
// 可以通过checker以增量方式添加包文件。文件夹。
func NewChecker(conf *Config, pkg *Package, info *Info) *Checker {
	// 确保我们有一个配置
	if conf == nil {
		conf = new(Config)
	}

	// 确保我们有一个信息结构
	if info == nil {
		info = new(Info)
	}

	version, err := parseGoVersion(conf.GoVersion)
	if err != nil {
		panic(fmt.Sprintf("invalid Go version %q (%v)", conf.GoVersion, err))
	}

	return &Checker{
		conf:    conf,
		ctxt:    conf.Context,
		pkg:     pkg,
		Info:    info,
		version: version,
		objMap:  make(map[Object]*declInfo),
		impMap:  make(map[importKey]*Package),
		infoMap: make(map[*Named]typeInfo),
	}
}

// initFiles初始化checker的文件特定部分。
// 提供的文件必须全部属于同一个包。
func (check *Checker) initFiles(files []*syntax.File) {
	// 从头开始（check.Files可能会被多次调用）
	check.files = nil
	check.imports = nil
	check.dotImportMap = nil

	check.firstErr = nil
	check.methods = nil
	check.untyped = nil
	check.delayed = nil
	check.objPath = nil
	check.cleaners = nil

	// 确定包名并收集有效文件
	pkg := check.pkg
	for _, file := range files {
		switch name := file.PkgName.Value; pkg.name {
		case "":
			if name != "_" {
				pkg.name = name
			} else {
				check.error(file.PkgName, "invalid package name _")
			}
			fallthrough

		case name:
			check.files = append(check.files, file)

		default:
			check.errorf(file, "package %s; expected %s", name, pkg.name)
			// 忽略此文件
		}
	}
}

// 紧急救援用于提前终止。
type bailout struct{}

func (check *Checker) handleBailout(err *error) {
	switch p := recover().(type) {
	case nil, bailout:
		// 正常返回或提前退出
		*err = check.firstErr
	default:
		// 重新恐慌
		panic(p)
	}
}

// 文件将检查提供的文件作为检查程序包的一部分。
func (check *Checker) Files(files []*syntax.File) error { return check.checkFiles(files) }

var errBadCgo = errors.New("cannot use FakeImportC and go115UsesCgo together")

func (check *Checker) checkFiles(files []*syntax.File) (err error) {
	if check.conf.FakeImportC && check.conf.go115UsesCgo {
		return errBadCgo
	}

	defer check.handleBailout(&err)

	print := func(msg string) {
		if check.conf.Trace {
			fmt.Println()
			fmt.Println(msg)
		}
	}

	print("== initFiles ==")
	check.initFiles(files)

	print("== collectObjects ==")
	check.collectObjects()

	print("== packageObjects ==")
	check.packageObjects()

	print("== processDelayed ==")
	check.processDelayed(0) // 包括所有函数

	print("== cleanup ==")
	check.cleanup()

	print("== initOrder ==")
	check.initOrder()

	if !check.conf.DisableUnusedImportCheck {
		print("== unusedImports ==")
		check.unusedImports()
	}

	print("== recordUntyped ==")
	check.recordUntyped()

	if check.firstErr == nil {
		// TODO（mdempsky）：确保存在错误时monomorph是安全的。
		check.monomorph()
	}

	check.pkg.complete = true

	// 不再需要-释放内存
	check.imports = nil
	check.dotImportMap = nil
	check.pkgPathMap = nil
	check.seenPkgMap = nil
	check.recvTParamMap = nil
	check.brokenAliases = nil
	check.unionTypeSets = nil
	check.ctxt = nil

	// TODO（gri）此时我们应该释放更多内存。

	return
}

// processDelayed进程所有延迟的操作都被推到top之后。
func (check *Checker) processDelayed(top int) {
	// 如果每个延迟的操作都推送一个新的操作，
	// 堆栈将在此循环中继续增长。
	// 然而，只有处理函数（其中
	// 以延迟方式处理）可能添加更多操作（例如嵌套函数），因此
	// 这是一个足够有界的过程。
	for i := top; i < len(check.delayed); i++ {
		a := &check.delayed[i]
		if check.conf.Trace && a.desc != nil {
			fmt.Println()
			check.trace(a.desc.pos.Pos(), "-- "+a.desc.format, a.desc.args...)
		}
		a.f() // 可以附加到支票上。延迟的
	}
	assert(top <= len(check.delayed)) // 堆栈不能收缩
	check.delayed = check.delayed[:top]
}

// 清除为所有收集的清理器运行清理。
func (check *Checker) cleanup() {
	// 由于命名，所以不要使用range子句。清理可能会添加更多清洁剂。
	for i := 0; i < len(check.cleaners); i++ {
		check.cleaners[i].cleanup()
	}
	check.cleaners = nil
}

func (check *Checker) record(x *operand) {
	// 将x转换为一组用户友好的值
	// TODO（gri）此代码可以简化
	var typ Type
	var val constant.Value
	switch x.mode {
	case invalid:
		typ = Typ[Invalid]
	case novalue:
		typ = (*Tuple)(nil)
	case constant_:
		typ = x.typ
		val = x.val
	default:
		typ = x.typ
	}
	assert(x.expr != nil && typ != nil)

	if isUntyped(typ) {
		// 延迟类型和值记录，直到我们知道类型
		// 或直到类型检查结束
		check.rememberUntyped(x.expr, false, x.mode, typ.(*Basic), val)
	} else {
		check.recordTypeAndValue(x.expr, x.mode, typ, val)
	}
}

func (check *Checker) recordUntyped() {
	if !debug && check.Types == nil {
		return // 无需执行
	}

	for x, info := range check.untyped {
		if debug && isTyped(info.typ) {
			check.dump("%v: %s (type %s) is typed", posFor(x), x, info.typ)
			unreachable()
		}
		check.recordTypeAndValue(x, info.mode, info.typ, info.val)
	}
}

func (check *Checker) recordTypeAndValue(x syntax.Expr, mode operandMode, typ Type, val constant.Value) {
	assert(x != nil)
	assert(typ != nil)
	if mode == invalid {
		return // 忽略
	}
	if mode == constant_ {
		assert(val != nil)
		// 我们在这里检查allBasic（typ，IsConstType）作为常量表达式可以是
		// 记录为类型参数。
		assert(typ == Typ[Invalid] || allBasic(typ, IsConstType))
	}
	if m := check.Types; m != nil {
		m[x] = TypeAndValue{mode, typ, val}
	}
}

func (check *Checker) recordBuiltinType(f syntax.Expr, sig *Signature) {
	// f必须是一个（可能是括号，可能是限定的）
	// 标识符，表示内置的（包括不安全的非常量
	// 函数添加和切片）：记录f和可能的
	// 子函数的签名。
	for {
		check.recordTypeAndValue(f, builtin, sig, nil)
		switch p := f.(type) {
		case *syntax.Name, *syntax.SelectorExpr:
			return // 我们完成了
		case *syntax.ParenExpr:
			f = p.X
		default:
			unreachable()
		}
	}
}

func (check *Checker) recordCommaOkTypes(x syntax.Expr, a [2]Type) {
	assert(x != nil)
	if a[0] == nil || a[1] == nil {
		return
	}
	assert(isTyped(a[0]) && isTyped(a[1]) && (isBoolean(a[1]) || a[1] == universeError))
	if m := check.Types; m != nil {
		for {
			tv := m[x]
			assert(tv.Type != nil) // 应该已经记录了
			pos := x.Pos()
			tv.Type = NewTuple(
				NewVar(pos, check.pkg, "", a[0]),
				NewVar(pos, check.pkg, "", a[1]),
			)
			m[x] = tv
			// 如果x是一个带括号的表达式（p.x），更新p.x 
			p, _ := x.(*syntax.ParenExpr)
			if p == nil {
				break
			}
			x = p.X
		}
	}
}

// recordInstance将实例化信息记录到检查中。信息，如果
// 实例映射为非零。给定的expr必须是ident、selector或
// 带有ident或selector操作数的index（list）expr。
// 
// TODO（rfindley）：expr参数很脆弱。看看我们能否以其他方式访问
// 实例化的标识符。
func (check *Checker) recordInstance(expr syntax.Expr, targs []Type, typ Type) {
	ident := instantiatedIdent(expr)
	assert(ident != nil)
	assert(typ != nil)
	if m := check.Instances; m != nil {
		m[ident] = Instance{newTypeList(targs), typ}
	}
}

func instantiatedIdent(expr syntax.Expr) *syntax.Name {
	var selOrIdent syntax.Expr
	switch e := expr.(type) {
	case *syntax.IndexExpr:
		selOrIdent = e.X
	case *syntax.SelectorExpr, *syntax.Name:
		selOrIdent = e
	}
	switch x := selOrIdent.(type) {
	case *syntax.Name:
		return x
	case *syntax.SelectorExpr:
		return x.Sel
	}
	panic("instantiated ident not found")
}

func (check *Checker) recordDef(id *syntax.Name, obj Object) {
	assert(id != nil)
	if m := check.Defs; m != nil {
		m[id] = obj
	}
}

func (check *Checker) recordUse(id *syntax.Name, obj Object) {
	assert(id != nil)
	assert(obj != nil)
	if m := check.Uses; m != nil {
		m[id] = obj
	}
}

func (check *Checker) recordImplicit(node syntax.Node, obj Object) {
	assert(node != nil)
	assert(obj != nil)
	if m := check.Implicits; m != nil {
		m[node] = obj
	}
}

func (check *Checker) recordSelection(x *syntax.SelectorExpr, kind SelectionKind, recv Type, obj Object, index []int, indirect bool) {
	assert(obj != nil && (recv == nil || len(index) > 0))
	check.recordUse(x.Sel, obj)
	if m := check.Selections; m != nil {
		m[x] = &Selection{kind, recv, obj, index, indirect}
	}
}

func (check *Checker) recordScope(node syntax.Node, scope *Scope) {
	assert(node != nil)
	assert(scope != nil)
	if m := check.Scopes; m != nil {
		m[node] = scope
	}
}
